
#pragma once

#include "./axis_plugin.hpp"
#include "../adsorb/adsorb.hpp"
#include "../layout/cell_command.hpp"
#include "../render/render.hpp"
#include "./axis.hpp"
#include "./models.hpp"

#include "../editing/move/move.hpp"
#include "../editing/slice/slice.hpp"

namespace qbe::axis {

void update_camera(vsg::ref_ptr<vsg::Camera> camera, vsg::ref_ptr<vsg::WindowTraits> traits) {
  auto viewport = vsg::ViewportState::create(0, 0, traits->width, traits->height);
  auto width_half = static_cast<double>(traits->width / 2);
  auto height_half = static_cast<double>(traits->height / 2);
  auto ortho = vsg::Orthographic::create(-width_half, width_half, -height_half, height_half, 0.0f, 10.0f);
  auto lookAt = vsg::LookAt::create(vsg::dvec3(0.0, 0.0, 5.0), vsg::dvec3(0.0, 0.0, 0.0), vsg::dvec3(0.0, 1.0, 0.0));
  auto new_camera = vsg::Camera::create(ortho, lookAt, viewport);
  (*camera) = (*new_camera);
};

void update_transform(vsg::ref_ptr<vsg::MatrixTransform> transform, double scale, double dx, double dy) {
  auto t_matrix = vsg::translate(vsg::vec3(dx, dy, 0.0));
  auto s_matrix = vsg::scale(vsg::vec3(scale, scale, 1.0));
  transform->matrix = t_matrix * s_matrix;
};

void init_axis(entt::registry& reg, ecs::Query query, ecs::Commands commands, ecs::Res<vsg::ref_ptr<vsg::Options>> options) {

  auto& render_model = query.get_resource<render::Model>();
  auto& model = query.get_resource<Model>();

  // mouse
  {
    auto vertices = vsg::vec2Array::create(4);
    vertices->properties.dataVariance = vsg::DYNAMIC_DATA;
    render::Mesh2D mesh(vertices);
    render::ShaderMaterial2D material{render::Uniform{
                                          vsg::vec3(1.0, 0.0, 0.0),
                                          vsg::vec3(1.0, 0.0, 0.0),
                                      },
                                      "shaders/axis/axis.vert.spv", "shaders/axis/axis.frag.spv"};
    model.render_mouse_node = render::MeshNode::create(options, mesh, material, VkPrimitiveTopology::VK_PRIMITIVE_TOPOLOGY_LINE_LIST);
    render_model.axis_group->addChild(model.render_mouse_node);
  }

  // axis
  {
    Axis axis;
    axis.width = render_model.windowTraits.get()->width;
    axis.height = render_model.windowTraits.get()->height;
    commands.insert_resource(axis);
  }

  // box
  {
    auto vertices = vsg::vec2Array::create(4);
    vertices->properties.dataVariance = vsg::DYNAMIC_DATA;
    render::Mesh2D mesh(vertices);
    render::ShaderMaterial2D material{render::Uniform{vsg::vec3(1.0, 1.0, 0.0), vsg::vec3(1.0, 1.0, 0.0)}, "shaders/axis/axis.vert.spv",
                                      "shaders/axis/axis.frag.spv"};
    auto node = render::MeshNode::create(options, mesh, material, VkPrimitiveTopology::VK_PRIMITIVE_TOPOLOGY_LINE_STRIP);
    render_model.transform->addChild(node);
    model.box_node = node;
  }
}

void update_axis(entt::registry& reg, ecs::Query query, ecs::Bus& bus) {
  auto& enable = query.get_resource<bool>();
  auto& name = query.get_resource<std::string>();
  auto& axis = query.get_resource<Axis>();
  auto& handle = query.get_resource<vsg::ref_ptr<HandleBundle>>();
  auto& state = query.get_resource<state::State>();
  auto& layout_cmd = query.get_resource<layout::LayoutCmd>();
  auto& cell = query.get_resource<qlib::Cell::s_ptr>();

  //设置吸附精度
  auto ui_messages = bus.receive<qbe::UiMessage::AdsorptionAccuracyMessage>();
  for (auto mes : ui_messages) {
    axis.adsorp_precision = mes.acc;
  }

  int32_t mx, my;
  if (handle->mouseMoved(&mx, &my)) {
    // SPDLOG_INFO("d: ({}, {})", axis.dx, axis.dy);
    // SPDLOG_INFO("real: ({}, {})", axis.real_x, axis.real_y);
    // SPDLOG_INFO("pos: ({}, {})", axis.pos_x, axis.pos_y);
    // SPDLOG_INFO("pos_real: ({}, {})", axis.pos_real_x, axis.pos_real_y);
    // SPDLOG_INFO("gl_d: ({}, {})", axis.gl_dx, axis.gl_dy);
    // SPDLOG_INFO("gl_mouse: ({}, {})", axis.gl_mouse_px, axis.gl_mouse_py);
    axis.onMouseMove(mx, my);

    axis.real_x = axis.adsorp_point.x;
    axis.real_y = axis.adsorp_point.y;
  }

  int32_t detal_y;
  if (handle->mouseWheel(&detal_y)) {
    axis.onMouseWheel(detal_y);
  }

  uint32_t x, y, width, height;
  if (handle->resized(&x, &y, &width, &height)) {
    axis.resize(width, height);
  }

  // input::KeyBoardShortcuts kbs_back_zero;
  // kbs_back_zero.keyBase = vsg::KEY_z;
  // kbs_back_zero.keyModifier = vsg::MODKEY_Shift;
  // if (handle->keyReleased(vsg::KEY_z)) {
  //   SPDLOG_INFO("Back Zero~");
  //   axis.backZero();
  // }
}

void view_box(entt::registry& reg, ecs::Query query, ecs::Bus& bus) {
  auto& axis = query.get_resource<Axis>();
  auto& handle = query.get_resource<vsg::ref_ptr<HandleBundle>>();
  auto& state = query.get_resource<state::State>();
  auto& model = query.get_resource<axis::Model>();

  bool run_box = false;
  bool run_draw_box = false;

  double x = axis.real_x;
  double y = axis.real_y;
  double px = axis.gl_mouse_px;
  double py = axis.gl_mouse_py;

  if (handle->mousePressed(vsg::ButtonMask::BUTTON_MASK_1)) {
    model.mouse_press = true;
    model.mouse_press_x = x;
    model.mouse_press_y = y;
  }

  if (handle->mouse_move) {
    // 鼠标按下后移动
    if (model.mouse_press) {
      run_draw_box = true;
    }
  }

  if (handle->mouseReleased(1)) {
    SPDLOG_INFO("[overall] mouse_press: {}", model.mouse_press);

    // 存在box，框选
    if (model.box_exist) {
      run_box = true;
      run_draw_box = false;
      model.box_render = false;
    }

    // 清除鼠标事件
    if (model.mouse_press)
      model.mouse_press = false;
    if (model.mouse_move)
      model.mouse_move = false;
    if (model.box_exist)
      model.box_exist = false;
  }

  if (run_draw_box) {
    if (!model.box_exist) {
      // 创建框选对象
      model.box_rect = qlib::Rectangle::create(qlib::Vec2d{x, y}, 0.0, 0.0);
      model.box_exist = true;
    }
    // 更新绘制的box
    if (model.box_exist) {
      auto& rect = model.box_rect;
      double w = x - model.mouse_press_x;
      double h = y - model.mouse_press_y;
      rect->pos.x = model.mouse_press_x;
      rect->pos.y = model.mouse_press_y;
      if (w < 0) {
        rect->pos.x = model.mouse_press_x + w;
        model.box_direct = DrawBoxDirect::Left;
      } else {
        model.box_direct = DrawBoxDirect::Right;
      }
      if (h < 0) {
        rect->pos.y = model.mouse_press_y + h;
      }
      rect->width = std::abs(w);
      rect->height = std::abs(h);
      // 更新box的mesh
      // model.box_mesh = render::BoxMesh(rect->bounding_box());
      // model.box_render = true;

      auto box = qlib::box_to_points(rect->bounding_box());
      model.box_node->updateMesh(render::Mesh2D(box));
    }
  }
  if (run_box) {
    // 设置视图区域
    auto& rect = model.box_rect;
    axis.setViewBox(rect->pos.x, rect->pos.y, rect->width, rect->height);

    model.box_node->clear();
  }
}

void view_drag(entt::registry& reg, ecs::Query query, ecs::Bus& bus) {
  auto& axis = query.get_resource<Axis>();
  auto& handle = query.get_resource<vsg::ref_ptr<HandleBundle>>();
  auto& state = query.get_resource<state::State>();
  auto& model = query.get_resource<axis::Model>();

  double px = axis.gl_mouse_px;
  double py = axis.gl_mouse_py;
  // 鼠标中键按下
  if (handle->mousePressed(vsg::ButtonMask::BUTTON_MASK_2)) {
    model.drag = true;
    model.drag_px = px;
    model.drag_py = py;
  }

  // 拖拽
  if (handle->mouse_move) {
    if (model.drag) {
      auto dx = px - model.drag_px;
      auto dy = py - model.drag_py;
      axis.tranlate(dx, dy);

      SPDLOG_INFO("dx:{}, dy:{}", dx, dy);
      model.drag_px = px;
      model.drag_py = py;
    }
  }

  if (handle->mouseReleased(2)) {
    model.drag = false;
  }
}

void view_move(ecs::Bus bus, ecs::Query query) {
  auto& axis = query.get_resource<Axis>();
  auto& handle = query.get_resource<vsg::ref_ptr<input::HandleBundle>>();
  auto& move_models = query.get_resource<move::Param>();
  auto& render_model = query.get_resource<render::Model>();
  auto& state = query.get_resource<state::State>();
  auto update_transform = [&]() {
    auto t_matrix = vsg::translate(vsg::vec3(axis.gl_dx, axis.gl_dy, 0.0));
    auto s_matrix = vsg::scale(vsg::vec3(axis.scale, axis.scale, 1.0));
    render_model.transform->matrix = t_matrix * s_matrix;
  };

  // auto dx = 0.2 * axis.width / axis.scale;
  // auto dy = 0.2 * axis.hight / axis.scale;
  auto dx = 0.2 * axis.width;
  auto dy = 0.2 * axis.height;
  bool editing_move_key_enable = (state.getEditingMode() == state::EditingModeType::Move) && move_models.is_key_move();
  if (getKeyPressed("view", "Left shift of the drawing board", bus) && !editing_move_key_enable) {
    axis.tranlate(dx, 0);
    update_transform();
  }
  if (getKeyPressed("view", "Sketchpad Move Right", bus) && !editing_move_key_enable) {
    axis.tranlate(-dx, 0);
    update_transform();
  }
  if (getKeyPressed("view", "Move the drawing board up", bus) && !editing_move_key_enable) {
    axis.tranlate(0, -dy);
    update_transform();
  }
  if (getKeyPressed("view", "Draw board down", bus) && !editing_move_key_enable) {
    axis.tranlate(0, dy);
    update_transform();
  }
}

// 区域自适应
void view_adapt(entt::registry& reg, ecs::Query query, ecs::Bus& bus) {
  auto& axis = query.get_resource<axis::Axis>();
  auto& handle = query.get_resource<vsg::ref_ptr<input::HandleBundle>>();
  auto& cell = query.get_resource<qlib::Cell::s_ptr>();
  auto& render_model = query.get_resource<render::Model>();
  auto layout_adapt_msg = bus.receive<qbe::UiMessage::LayoutAdaptMessage>();

  auto update_camera = [&](vsg::ref_ptr<vsg::WindowTraits> traits) {
    auto viewport = vsg::ViewportState::create(0, 0, traits->width, traits->height);
    auto width_half = static_cast<double>(traits->width / 2);
    auto height_half = static_cast<double>(traits->height / 2);
    auto ortho = vsg::Orthographic::create(-width_half, width_half, -height_half, height_half, 0.0f, 10.0f);
    auto lookAt = vsg::LookAt::create(vsg::dvec3(0.0, 0.0, 5.0), vsg::dvec3(0.0, 0.0, 0.0), vsg::dvec3(0.0, 1.0, 0.0));
    auto new_camera = vsg::Camera::create(ortho, lookAt, viewport);
    (*render_model.camera) = (*new_camera);
  };

  auto update_map_camera = [&](vsg::ref_ptr<vsg::WindowTraits> traits) {
    auto s = axis.grid_scale;
    auto tw = static_cast<double>(traits->width);
    auto th = static_cast<double>(traits->height);
    // auto dx = (s - 1) * axis.gl_dx;
    // auto dy = (s - 1) * axis.gl_dy;
    auto left = s * (-tw / 2.0f);
    auto right = s * (tw / 2.0f);
    auto top = s * (-th / 2.0f);
    auto bottom = s * (th / 2.0f);
    auto viewport = vsg::ViewportState::create(0, 0, tw, th);
    auto ortho = vsg::Orthographic::create(left, right, top, bottom, 0.0f, 10.0f);
    auto lookAt = vsg::LookAt::create(vsg::dvec3(0.0, 0.0, 5.0), vsg::dvec3(0.0, 0.0, 0.0), vsg::dvec3(0.0, 1.0, 0.0));
    auto new_camera = vsg::Camera::create(ortho, lookAt, viewport);
    (*render_model.map_camera) = (*new_camera);
  };

  auto update_transform = [&]() {
    auto t_matrix = vsg::translate(vsg::vec3(axis.gl_dx, axis.gl_dy, 0.0));
    auto s_matrix = vsg::scale(vsg::vec3(axis.scale, axis.scale, 1.0));
    for (auto [e, transform] : reg.view<render::SceneTag, vsg::ref_ptr<vsg::MatrixTransform>>().each()) {
      transform->matrix = t_matrix * s_matrix;
    }
  };

  auto update_adapt = [&]() {
    auto geo_size = cell->reg.view<qlib::Tag>().size();
    auto ref_size = cell->reg.view<qlib::Reference>().size();
    if (geo_size == 0 && ref_size == 0) {
      axis.setCenter();
      axis.updateValue();
    } else {
      auto box = cell->bounding_box();
      auto max = box[1];
      auto min = box[0];
      if (max.x <= min.x || max.y <= min.y) {
        min -= 0.5;
        max += 0.5;
      }
      axis.adaptBox(min.x, min.y, max.x, max.y);
    }
  };

  auto& traits = query.get_resource<vsg::ref_ptr<vsg::WindowTraits>>();
  // if (handle->mouse_wheel || handle->mouse_move || handle->mouse_press || handle->mouse_release) {
  //   // update_camera(render_model.window->traits);
  //   // update_camera(traits);
  //   // update_map_camera(traits);
  //   // update_transform();
  // }

  uint32_t x, y, width, height;
  if (handle->resized(&x, &y, &width, &height)) {
    // if (width < 300)
    //   return;
    // traits->x = x;
    // traits->y = y;
    // traits->width = width;
    // traits->height = height;

    // render_model.window->traits->x = x;
    // render_model.window->traits->y = y;
    // render_model.window->traits->width = width;
    // render_model.window->traits->height = height;

    // update_adapt();

    // update_camera(render_model.window->traits);
    // update_map_camera(render_model.window->traits);
    // update_map_camera(render_model.map_window->traits);
    // update_transform();
  }

  if (!layout_adapt_msg.empty()) {
    update_adapt();

    // update_transform();
    // update_camera(traits);
  }
  update_transform();
  update_camera(render_model.window->traits);
  // update_map_camera(render_model.window->traits);
  update_map_camera(render_model.map_window->traits);
  // update_camera(traits);
}

void render_axis(entt::registry& reg, ecs::Query query, ecs::Res<canvas::CanvasWidget*> canvas) {
  auto& axis = query.get_resource<axis::Axis>();
  auto& adsorb_model = query.get_resource<adsorb::AdsorbModel>();
  auto& axis_model = query.get_resource<Model>();

  auto cross_vertices = [](vsg::ref_ptr<vsg::vec2Array> vertices, double px, double py) {
    double w = 50.0;
    (*vertices)[0] = vsg::vec2(px, py - w);
    (*vertices)[1] = vsg::vec2(px, py + w);
    (*vertices)[2] = vsg::vec2(px - w, py);
    (*vertices)[3] = vsg::vec2(px + w, py);
    vertices->dirty();
  };

  auto slice_vertices = [](vsg::ref_ptr<vsg::vec2Array> vertices, bool isX, qlib::Vec2d pos) {
    double max_vlaue = 1e8;
    if (!isX) {
      (*vertices)[0] = vsg::vec2(pos.x, -max_vlaue);
      (*vertices)[1] = vsg::vec2(pos.x, max_vlaue);
      (*vertices)[2] = vsg::vec2(pos.x, DBL_MAX);
      (*vertices)[3] = vsg::vec2(pos.x, DBL_MAX);
    } else {
      (*vertices)[0] = vsg::vec2(-max_vlaue, pos.y);
      (*vertices)[1] = vsg::vec2(max_vlaue, pos.y);
      (*vertices)[2] = vsg::vec2(DBL_MAX, pos.y);
      (*vertices)[3] = vsg::vec2(DBL_MAX, pos.y);
    }
    vertices->dirty();
  };

  auto& state = query.get_resource<state::State>();
  if (state.getEditingMode() == state::EditingModeType::Slice) {
    auto p = axis.real2pixel(axis.real_x, axis.real_y);
    auto& slice_models = query.get_resource<qbe::slice::Models>();
    slice_vertices(axis_model.render_mouse_node->mesh.vertices, slice_models.isXSlice, {p.x, p.y});
  } else {
    auto p = axis.real2pixel(axis.real_x, axis.real_y);
    cross_vertices(axis_model.render_mouse_node->mesh.vertices, p.x, p.y);
  }

  if (axis_model.box_render) {
    auto box = qlib::box_to_points(axis_model.box_rect->bounding_box());
    axis_model.box_node->updateMesh(render::Mesh2D(box));
    axis_model.box_render = false;
  }

  canvas.get()->rulerDraw(axis.rulerX.paths, axis.rulerY.paths, QVector2D(axis.mouse_pixel_x, axis.mouse_pixel_y));
}

// // 区域自适应
// void view_update(entt::registry& reg, ecs::Query query, ecs::Bus& bus) {
//   auto& axis = query.get_resource<axis::Axis>();
//   auto& axis_model = query.get_resource<axis::Model>();
//   auto& handle = query.get_resource<vsg::ref_ptr<input::HandleBundle>>();
//   auto& cell = query.get_resource<qlib::Cell::s_ptr>();
//   auto& render_model = query.get_resource<render::Model>();
//   auto layout_adapt_msg = bus.receive<qbe::UiMessage::LayoutAdaptMessage>();

//   auto update_camera = [&](vsg::ref_ptr<vsg::WindowTraits> traits) {
//     auto viewport = vsg::ViewportState::create(0, 0, traits->width, traits->height);
//     auto width_half = static_cast<double>(traits->width / 2);
//     auto height_half = static_cast<double>(traits->height / 2);
//     auto ortho = vsg::Orthographic::create(-width_half, width_half, -height_half, height_half, 0.0f, 10.0f);
//     auto lookAt = vsg::LookAt::create(vsg::dvec3(0.0, 0.0, 5.0), vsg::dvec3(0.0, 0.0, 0.0), vsg::dvec3(0.0, 1.0, 0.0));
//     auto new_camera = vsg::Camera::create(ortho, lookAt, viewport);
//     (*render_model.camera) = (*new_camera);
//   };

//   auto update_transform = [&]() {
//     auto t_matrix = vsg::translate(vsg::vec3(axis.gl_dx, axis.gl_dy, 0.0));
//     auto s_matrix = vsg::scale(vsg::vec3(axis.scale, axis.scale, 1.0));
//     for (auto [e, transform] : reg.view<render::SceneTag, vsg::ref_ptr<vsg::MatrixTransform>>().each()) {
//       transform->matrix = t_matrix * s_matrix;
//     }
//   };

//   auto& traits = query.get_resource<vsg::ref_ptr<vsg::WindowTraits>>();
//   if (handle->mouse_wheel || handle->mouse_move || handle->mouse_press || handle->mouse_release) {
//     update_camera(traits);
//     update_transform();
//   }

//   uint32_t x, y, width, height;
//   if (handle->resized(&x, &y, &width, &height)) {
//     update_camera(render_model.window->traits);
//     update_transform();
//   }

//   if (axis_model.axis_update) {
//     update_transform();
//     update_camera(traits);
//   }
// }

void AxisPlugin::build(ecs::App& app) {
  app.init_resource<AreaScaleModel>(AreaScaleModel{});
  app.init_resource<DragAxisModel>(DragAxisModel{});
  app.init_resource<Model>(Model{});

  app.add_systems<ecs::stage::Startup>(init_axis);
  app.add_systems<ecs::stage::PreUpdate>(update_axis, view_drag, view_move, view_adapt);
  // app.add_systems<ecs::stage::PostUpdate>(view_box);
  app.add_systems<ecs::stage::PostUpdate>(render_axis);
};

}  // namespace qbe::axis